use std::{fs::File, io::BufReader};

use serde::Deserialize;

use crate::processor::memory_limiter::MemoryLimiterConfig;
use crate::processor::batch_processor::BatchProcessorConfig;

/// Configuration for all receiver endpoints.
#[derive(Debug, Deserialize, Clone, Default)]
pub struct Receivers {
    #[serde(default)]
    pub grpc: String,
    #[serde(default)]
    pub http: String,
    #[serde(default)]
    pub command: String,
}

/// Configuration for all exporters.
#[derive(Debug, Deserialize, Clone, Default)]
pub struct Exporters {
    #[serde(default)]
    pub debug: bool,
    #[serde(default)]
    pub grpc:ExporterConfig,
    #[serde(default)]
    pub http: ExporterConfig,
}

/// Configuration for a specific exporter.
#[derive(Debug, Deserialize, Clone, Default)]
pub struct ExporterConfig {
    #[serde(default)]
    pub endpoint: String,
    #[serde(default)]
    pub queue: QueueConfig,
}

/// Configuration for exporter queues.
#[derive(Debug, Deserialize, Clone, Default)]
pub struct QueueConfig {
    #[serde(default)]
    pub capacity_items: u32,
    #[serde(default)]
    pub capacity_bytes: u32,
    #[serde(default)]
    pub persistent: String,
}

/// Configuration for all processors.
#[derive(Debug, Deserialize, Clone, Default)]
pub struct Processors {
    #[serde(default)]
    pub memory_limiter: MemoryLimiterConfig,
    #[serde(default)]
    pub batch: BatchProcessorConfig,
}

/// Main configuration structure for the collector.
#[derive(Debug, Deserialize, Clone)]
pub struct Config {
    pub receivers: Receivers,
    pub exporters: Exporters,
    pub processors: Processors,
}

impl Config {
    /// Loads configuration from a YAML file.
    pub fn from_file(path: &str) -> Result<Self, Box<dyn std::error::Error + Send + Sync>> {
        let file = File::open(path)?;
        let reader = BufReader::new(file);
        let config: Config = serde_yaml::from_reader(reader)?;
        Ok(config)
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::fs::File;
    use std::io::BufReader;

    #[test]
    fn test_load_config_from_file() {
        let file = File::open("tests/collector-config.yml").expect("open config");
        let reader = BufReader::new(file);
        let config: Config = serde_yaml::from_reader(reader).expect("deserialize config");
        assert!(!config.receivers.grpc.is_empty() || !config.receivers.http.is_empty());
        assert!(config.exporters.grpc.endpoint.is_empty() || config.exporters.http.endpoint.is_empty() || !config.exporters.grpc.endpoint.is_empty() || !config.exporters.http.endpoint.is_empty());
        assert!(config.processors.memory_limiter.limit_mib > 0 || config.processors.batch.send_batch_size > 0);
    }

    #[test]
    fn test_default_config() {
        let config = Config {
            receivers: Receivers::default(),
            exporters: Exporters::default(),
            processors: Processors::default(),
        };
        assert_eq!(config.receivers.grpc, "");
        assert_eq!(config.exporters.debug, false);
        assert_eq!(config.exporters.grpc.endpoint, "");
        assert_eq!(config.processors.memory_limiter.limit_mib, 100);
    }
}
